package cn.yangliu.shiro.config;

import cn.yangliu.comm.tools.SpringUtil;
import cn.yangliu.shiro.AuthExceptionAdvice;
import cn.yangliu.shiro.redis.RedisManager;
import cn.yangliu.shiro.web.ShiroConrtoller;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;

import javax.annotation.Resource;
import javax.servlet.DispatcherType;
import java.util.Properties;

/**
 * 作者 杨柳
 * 创建时间 2017-08-24 20:45
 */
@Configuration
@EnableConfigurationProperties(value = ShiroProperties.class)
@ConditionalOnProperty(prefix = "spring.shiro", value = "enable", matchIfMissing = true)
public class ShiroConfig {

    @Resource
    private ShiroProperties properties;

    @Bean
    public ShiroConrtoller shiroConrtoller() {
        return new ShiroConrtoller();
    }

    /**
     * 自定义url 将login_url和shiroController绑定
     */
    @Bean
    public SimpleUrlHandlerMapping shiroHandlerMapping() {
        SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping();
        handlerMapping.setOrder(Integer.MAX_VALUE - 2);
        Properties prop = new Properties();
        prop.put(properties.getLoginUrl(), "shiroConrtoller");
        handlerMapping.setMappings(prop);
        return handlerMapping;
    }

    /**
     * 注册shiro的filter
     */
    @Bean
    public FilterRegistrationBean shiroFilterRegistrationBean() {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        filterRegistration.setFilter(new DelegatingFilterProxy("shiroFilter"));
        filterRegistration.setEnabled(true);
        filterRegistration.addUrlPatterns(properties.getUrlPattern());
        filterRegistration.setDispatcherTypes(DispatcherType.REQUEST);
        return filterRegistration;
    }

    @Bean
    @ConditionalOnMissingBean(CredentialsMatcher.class)
    public CredentialsMatcher credentialsMatcher() {
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName(properties.getHashAlgorithmName());
        credentialsMatcher.setHashIterations(properties.getHashIterations());
        return credentialsMatcher;
    }

    @Bean
    public SecurityManager securityManager(Realm realm, CacheManager shiroRedisCacheManger, SessionManager sessionManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm);
        securityManager.setCacheManager(shiroRedisCacheManger);
        securityManager.setSessionManager(sessionManager);
        return securityManager;
    }

    @Bean
    public Realm realm(CredentialsMatcher credentialsMatcher, RedisCacheManager shiroRedisCacheManger) throws Exception {
        Class<? extends AuthorizingRealm> clazz = properties.getRealmClassName();
        AuthorizingRealm realm = clazz.newInstance();
        realm.setCredentialsMatcher(credentialsMatcher);
        realm.setCacheManager(shiroRedisCacheManger);
        return realm;
    }

    @Bean
    public SessionManager sessionManager(SessionDAO sessionDAO) {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionDAO(sessionDAO);
        Cookie sessionIdCookie = sessionManager.getSessionIdCookie();
        sessionIdCookie.setName(properties.getCookieName());
        sessionManager.setGlobalSessionTimeout(properties.getGlobalSessionTimeout());
        return sessionManager;
    }

    @Bean
    public RedisManager shiroRedisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setExpire(properties.getRedisExpire());
        redisManager.setHost(properties.getRedisHost());
        redisManager.setPassword(properties.getRedisPassword());
        redisManager.setPort(properties.getRedisPort());
        redisManager.setTimeout(properties.getRedisTimeout());
        redisManager.setDatabase(properties.getRedisDatabase());
        return redisManager;
    }

    @Bean
    public RedisSessionDAO sessionDAO(RedisManager redisManager) {
        RedisSessionDAO sessionDAO = new RedisSessionDAO();
        sessionDAO.setRedisManager(redisManager);
        return sessionDAO;
    }

    @Bean
    public RedisCacheManager shiroRedisCacheManger(RedisManager redisManager) {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager);
        return redisCacheManager;
    }

    @Bean
    public AuthExceptionAdvice authExceptionAdvice() {
        return new AuthExceptionAdvice();
    }

    @Bean
    @ConditionalOnMissingBean(SpringUtil.class)
    public SpringUtil springUtil() {
        return new SpringUtil();
    }


}
